/*
 Copyright 2009 Christian Sprecher

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */
package helpers

import javax.jcr.Session
import javax.jcr.Node as JcrNode
import sun.net.www.MimeTable
import javax.jcr.Property
import javax.jcr.PropertyType
import javax.jcr.Value
import javax.jcr.ValueFactory

/**
 * Created by IntelliJ IDEA.
 * User: christian
 * Date: Jan 9, 2009
 * Time: 5:26:41 AM
 * To change this template use File | Settings | File Templates.
 */

public class JcrHelper {
    Session session

    public JcrHelper(Session session) {
        this.session = session
    }
    
    public withItem= { String path, Closure action ->
        if(path && session.itemExists(path)) {
           def item = session.getItem(path)
           action(item)
        }
    }
    public withProperty =  { String path, Closure action ->
        withItem(path,action)
    }

    public withNode =  { String path, Closure action ->
        withItem(path,action)
    }

    /**
     * creates new property with default values. If the property already exists, nothing is done
     */
    public createProperty(String propertyPath, int typeHint, boolean mvp=false) {
        def nodePath = propertyPath.substring(0,propertyPath.lastIndexOf("/"))
        def propertyName = propertyPath.substring(propertyPath.lastIndexOf("/")+1, propertyPath.size())
        this.withNode(nodePath) { node ->
            if(!node.hasProperty(propertyName)) {
                saveProperty(propertyPath, null, typeHint, mvp)
            }
        }
    }
    /**
    *  Saves values to property. If the property already exists, it is overwritten, i.e. if values is null,
     * the property is deleted. If the property is multivalued, the "values" parameter has to be either an array
     * or an object (both work). If the property is single valued and a array is passed in values, the first item
     * is taken. The Items have to be of the appropriate types,<br/>
     */
    public saveProperty(String propertyPath, values, int typeHint=-1, boolean mvp=false) {
        def nodePath = propertyPath.substring(0,propertyPath.lastIndexOf("/"))
        def propertyName = propertyPath.substring(propertyPath.lastIndexOf("/")+1, propertyPath.size())
        this.withNode(nodePath) { node ->
            int i = 0
            boolean propertyExists = node.hasProperty(propertyName)
            boolean multiValued = mvp
            int type = typeHint<0?PropertyType.STRING:typeHint
            Property property
            ValueFactory vf = session.valueFactory
            def value
            if(propertyExists) {
                //overwrite from existing
                property = node.getProperty(propertyName)
                if(typeHint < 0) {
                    type = property.type
                }
                multiValued = property.definition.multiple
            }
            if(values == null && propertyExists) {
                node.getProperty(propertyName).remove()
            } else {
                if( values != null) {
                    if(multiValued) {
                        if(values instanceof java.util.List) {
                            value = new Value[values.size()]
                            int j=0
                            values.each { currentValue  ->
                                value[j++] = vf.createValue(currentValue)
                            }
                        } else {
                            value = new Value[1]
                           value[0] =vf.createValue(values)
                        }
                    }  else {
                        if(values instanceof java.util.List) {
                            value = vf.createValue(values[0])
                        } else {
                           value =vf.createValue(values)
                        }
                    }
                } else {
                    def oneVal
                    switch(type) {
                        case(PropertyType.BOOLEAN):
                            oneVal = vf.createValue(true)
                        break
                        case(PropertyType.DATE):
                            oneVal = vf.createValue(Calendar.getInstance())
                        break
                        case([PropertyType.LONG,PropertyType.DOUBLE]):
                            oneVal = vf.createValue(0)
                        break
                        case(PropertyType.REFERENCE):
                        //TODO: what strategy to use here?
                            oneVal = vf.createValue(node)
        //                node.setProperty(model.currentProperty.name, node)
                        break
                        case(PropertyType.NAME):
                            oneVal = vf.createValue(node.name)
                        break
                        default:
                            oneVal = vf.createValue("", type)
                    }
                    if(multiValued) {
                        value = new Value[1]
                        value[0] = oneVal
                    }   else {
                        value = oneVal

                    }
                }
                node.setProperty(propertyName, value)
            }
            node.save()
        }
    }

    public addFile(String path, String filePath, boolean recursive=true, String folderType="nt:folder") {
        withNode(path) { node ->
            File file = new File(filePath)
            Calendar calendar = Calendar.getInstance()
            if(file.isDirectory()) {
                JcrNode directoryNode = node.addNode(file.name,folderType)
                node.save()
                if(recursive) {
                    file.list().each() { child ->
                        addFile(directoryNode.path, file.absolutePath + "/"+ child,recursive,folderType)
                    }
                }
            }  else {
                JcrNode fileNode = node.addNode(file.name, "nt:file")
                
                //fileNode.setProperty("jcr:created",.setTimeInMillis(file.lastModified()))
                MimeTable mt = MimeTable.defaultTable
                def mimeType = mt.getContentTypeFor(file.name)?:"application/octet-stream"
                JcrNode resourceNode = fileNode.addNode("jcr:content", "nt:resource")
                resourceNode.setProperty("jcr:mimeType", mimeType);
                resourceNode.setProperty("jcr:encoding", "");
                calendar.setTimeInMillis(file.lastModified())
                resourceNode.setProperty("jcr:lastModified",calendar)
                resourceNode.setProperty("jcr:data", new FileInputStream(file))
                node.save()
            }

        }
    }
}